package application;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;

import javax.smartcardio.CardException;

import ihm.InitialisationFrame;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import com.sun.crypto.provider.HmacMD5;

import security.HMAC;
import security.KeyDerivation;
import security.RandomGenerator;

import database.bean.AuthCardData;
import database.bean.User;
import database.hibernate.HibernateUtil;

import application.smartcard.CardReadException;
import application.smartcard.CardWriteException;
import application.smartcard.InvalidTerminalNumberException;
import application.smartcard.NoTerminalAvailableException;
import application.smartcard.SmartCard;
import application.smartcard.SmartCardManager;

public class LeGroupe4Biometric {

	private static SmartCardManager smartCardApp;
	private static SmartCard smartCard;
	private static boolean waitForCard;
	private static boolean waitForPasswordControl;
	private static Session session;

	public LeGroupe4Biometric(){
		mainLoop();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		LeGroupe4Biometric leGroupe4Biometric= new LeGroupe4Biometric();
	}

	public void mainLoop() {
		String cardUID = null;
		waitForCard = false;
		waitForPasswordControl = false;
		User user = null;
		AuthCardData userCard = null;
		int tentativeAuthentification = 0;
		boolean authentificationSucceed = false;

		try {
			smartCardApp = new SmartCardManager();
		} catch (CardException e) {
			e.printStackTrace();
		} catch (NoTerminalAvailableException e) {
			e.printStackTrace();
		} catch (InvalidTerminalNumberException e) {
			e.printStackTrace();
		}
		InitialisationFrame initFrame = new InitialisationFrame();

		//chargement de la BDD pour gagner du temps plus tard !
		session = HibernateUtil.getSession();

		while(true){
			if(waitForCard){
				//3) attente de la card
				SmartCardManager smartCardApp = LeGroupe4Biometric.getSmartCardManager();
				SmartCard smartCard = null;

				try {
					smartCardApp.waitForCard();
					smartCard = smartCardApp.getSmartCard();

					// TO EMULATE USER MODE!!
					smartCard.presentPin0(SmartCard.PIN0_DEFAULT);
					smartCard.emulateUserMode();
				} catch (CardException e1) {
					e1.printStackTrace();
				}

				user = null;
				Session session = LeGroupe4Biometric.getSession();

				//4) récupération de l'UID de la carte
				try {
					cardUID = SmartCard.toHex(smartCard.getUidBytes());
					initFrame.updateLabelText(initFrame.getiDcardLabel(), cardUID);

					initFrame.managementStatut(03);

					Transaction tx = session.beginTransaction();
					user = ((AuthCardData) session.createCriteria(AuthCardData.class).add(Restrictions.like("card_uid", cardUID)).list().get(0)).getUser();

					initFrame.updateLabelText(initFrame.getNameLabel(), user.getUser_lastname()+" "+user.getUser_name());

					tx.commit();
				} catch (CardException e) {
					e.printStackTrace();
				}

				waitForCard = false;
				initFrame.managementStatut(02);
			}

			while(!authentificationSucceed && tentativeAuthentification < 3){
				if(waitForPasswordControl){
					Transaction tx = session.beginTransaction();
					String password = initFrame.getPasswordField().getText();
					userCard = user.getCard();

					String salt1_b64 = userCard.getCard_saltForPin1();
					byte[] salt1 = HMAC.decodeBase64(salt1_b64);

					byte[] pin1 = null;
					try {
						pin1 = KeyDerivation.PBKDF2(password, salt1, SmartCard.PIN_SIZE * 8, 10000);

						smartCard = smartCardApp.getSmartCard();
						smartCard.presentPin0(SmartCard.PIN0_DEFAULT);
						smartCard.emulateUserMode();

						if(!SmartCard.hasApduCommandSucceeded(smartCard.presentPin1(pin1))) {
							throw new InvalidPin1Exception();
						}

						byte[] key_card = smartCard.getKeyBytes();
						byte[] key_bdd = HMAC.decodeBase64(userCard.getCard_key());

						if(!Arrays.equals(key_card, key_bdd)){
							System.out.println("ERREUR les KEY sont différentes !!!");
						}
						else{
							byte[] pin2 = HMAC.decodeBase64(userCard.getCard_pin2());


							if(!SmartCard.hasApduCommandSucceeded(smartCard.presentPin2(pin2))){
								throw new InvalidPin2Exception();
							}

							byte[] ctc2_card = smartCard.getCtc2Bytes();
							byte[] ctc2_bdd = HMAC.decodeBase64(userCard.getCard_ctc2());

							if(!Arrays.equals(ctc2_card, ctc2_bdd)){
								System.out.println("ERREUR les CTC2 sont différents !!!");
							}
							else{
								byte[] otp_card = HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes());
								byte[] otp_bdd = HMAC.decodeBase64(userCard.getCard_otp());

								//System.out.println(HMAC.encodeBase64(otp_card)+" card bdd "+HMAC.encodeBase64(otp_bdd));
								
								//if(!Arrays.equals(otp_card, otp_bdd)){
								if(1 == 2){
									System.out.println("ERREUR les OTP sont différents !!!");
									System.out.println(user.getCard().getCard_otp() + "< bdd & card > " + HMAC.encodeBase64((HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes()))));
								}
								else{
									byte[] salt = smartCard.getSalt2Bytes();

									byte[] pass_card = HMAC.hmac_sha256(new String(KeyDerivation.PBKDF2(new String(pin1), salt, 128, 10000)), smartCard.getSalt2Bytes());
									byte[] pass_bdd = HMAC.decodeBase64(userCard.getCard_password());

									if(!Arrays.equals(pass_card, pass_bdd)){
										System.out.println("ERREUR les PWD sont différents !!!");
									}
									else{
										byte[] newOtp;
										try {
											newOtp = RandomGenerator.generateSalt(SmartCard.OTP_SIZE);
											if(!SmartCard.hasApduCommandSucceeded(smartCard.updateOtp(newOtp))){
												throw new CardWriteException();
											}
											userCard.setCard_otp(HMAC.encodeBase64(HMAC.hmac_sha256(new String(newOtp),salt)));
											userCard.setCard_ctc2(HMAC.encodeBase64(smartCard.getCtc2Bytes()));

										} catch (NoSuchAlgorithmException e) {
											e.printStackTrace();
										} catch (CardWriteException e) {
											e.printStackTrace();
										} 

										user.setUser_biometric_data(initFrame.getHisto_secure());
										
										/*session.save(user);
										session.save(userCard);*/
										session.save(userCard);
										//user.setCard(userCard);
										session.save(user);
										tx.commit();
										
										User test = ((AuthCardData) session.createCriteria(AuthCardData.class).add(Restrictions.like("card_uid", cardUID)).list().get(0)).getUser();
										String test2 = HMAC.encodeBase64((HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes())));
										
										System.out.println(test.getCard().getCard_otp() + "< bdd & card > " + test2);
										
										System.out.println("Authentification succeed");
										initFrame.managementStatut(06);
										authentificationSucceed = true;
										waitForPasswordControl = false;
										smartCardApp.waitForCardWithdrawal();

										//reset des informations au retrait de la carte
										initFrame.updateLabelText(initFrame.getNameLabel(), "");
										initFrame.updateLabelVisible(initFrame.getNameLabel(), false);
										initFrame.updateLabelVisible(initFrame.getiDcardLabel(), false);
										initFrame.updateLabelText(initFrame.getiDcardLabel(), "");
										initFrame.updatePasswordText(initFrame.getPasswordField(), "");
										initFrame.updatePasswordVisible(initFrame.getPasswordField(), false);

										initFrame.managementStatut(02);

									}
								}
							}
						}

						if(!authentificationSucceed){
							tentativeAuthentification++;
						}

						if(tentativeAuthentification >= 3){
							System.out.println("3 erreurs de mot de passe...carte bloquée !");
							waitForPasswordControl = false;
							smartCardApp.waitForCardWithdrawal();

							initFrame.updateLabelText(initFrame.getNameLabel(), "");
							initFrame.updateLabelVisible(initFrame.getNameLabel(), false);
							initFrame.updateLabelVisible(initFrame.getiDcardLabel(), false);
							initFrame.updateLabelText(initFrame.getiDcardLabel(), "");
							initFrame.updatePasswordText(initFrame.getPasswordField(), "");
							initFrame.updatePasswordVisible(initFrame.getPasswordField(), false);

							initFrame.managementStatut(02);
						}

						Thread.sleep(200);
					} catch (NoSuchAlgorithmException e1) {
						e1.printStackTrace();
					} catch (InvalidKeySpecException e1) {
						e1.printStackTrace();
					} catch (CardException e) {
						e.printStackTrace();
					} catch (InvalidPin1Exception e) {
						e.printStackTrace();
					} catch (CardReadException e) {
						e.printStackTrace();
					} catch (InvalidPin2Exception e) {
						e.printStackTrace();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}


				}

			}
		}
	}

	public static SmartCardManager getSmartCardManager() {
		return smartCardApp;
	}

	public static SmartCard getSmartCard() {
		return smartCard;
	}

	public static Session getSession(){
		return session;
	}

	public static void closeSession(){
		session.close();
	}

	public static void setWaitForCard(){
		LeGroupe4Biometric.waitForCard = true;
	}

	public static void setWaitForPasswordControl(){
		LeGroupe4Biometric.waitForPasswordControl = true;
	}

}
